/**
 * 2017年5月30日
 */
/**
 *
 * 这个包写控制器
 * 如果需要返回页面请新建view包等
 */
package cn.edu.bjtu.workbench.mvc.controller;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.deeplearning4j.nn.conf.ComputationGraphConfiguration;
import org.deeplearning4j.nn.conf.ConvolutionMode;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.Updater;
import org.deeplearning4j.nn.conf.graph.MergeVertex;
import org.deeplearning4j.nn.conf.layers.ConvolutionLayer;
import org.deeplearning4j.nn.conf.layers.GlobalPoolingLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.lossfunctions.LossFunctions;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import cn.edu.bjtu.workbench.api.CNNNetworkDesignHandler;
import cn.edu.bjtu.workbench.configuration.TextCategorizationCNNConfig;
import cn.edu.bjtu.workbench.core.TextCategorizationManager;
import cn.edu.bjtu.workbench.core.TextCategorizationManager.ClassificationPair;
import cn.edu.bjtu.workbench.model.TextCategorizationCNNModel;
import cn.edu.bjtu.workbench.mvc.BaseController;
import cn.edu.bjtu.workbench.mvc.ee.AsyncInvoke;
import cn.edu.bjtu.workbench.mvc.entity.ClassificationResultDTO;
/**
 * 以后在有其他结构的神经网络直接写controller实现CNNNetworkDesignHandler接口就可以了.
 * 目前这个做为默认
 *
 * @author alex
 *
 */
@RestController
@RequestMapping(value = "cnn")
public class DefaultClassificationController extends BaseController implements CNNNetworkDesignHandler {
	TextCategorizationCNNModel model = TextCategorizationCNNModel.get();
	TextCategorizationCNNConfig config = TextCategorizationCNNConfig.get();

	@RequestMapping(value = "/build")
	public String build(HttpServletRequest req) throws InterruptedException {
		//手动指定下类型
//		config.setCNN_TYPE(Deep4jModelType.ComputationGraph);
		try{
			model.configDataSetIteratorHandlerProvider(this);
			model.configNetworkDesignHandler(this);
			model.buildNetworkModel();
			return jsonDump(SUCCESS);
		}catch(Exception e){
			logException(e);
			return jsonDump(ERROR);
		}
	}
	@RequestMapping(value = "/predict")
	public String predict(HttpServletRequest req) throws InterruptedException {
		String doc = req.getParameter("doc");
		try{
			ClassificationPair[] res = model.predictDocumentLabelString(doc);
			logger.info("Document: {}  \n Predicted: {} \n",doc , Arrays.toString(res));
		//返回结果的格式待商量
			return jsonDump(new ClassificationResultDTO(res));
		}catch (Exception e){
			logException(e);
			return jsonDump(ERROR);
		}
	}
	@RequestMapping(value = "/update")
	public String update(HttpServletRequest req) throws InterruptedException {
		String doc = req.getParameter("doc");
		String label = req.getParameter("label");
		if(StringUtils.hasText(doc) && StringUtils.hasText(label) &&  TextCategorizationManager.get().checkLabel(label)){
			model.addDocumentForUpdate(doc, label);
			try{
				return jsonDump(SUCCESS);
			}catch(Exception e){
				logException(e);
				return jsonDump(ERROR);
			}
		}else{
			return jsonDump(ParamsError);
		}
	}
	@RequestMapping(value = "/save")
	public String save(HttpServletRequest req) throws InterruptedException {
		//未必能保存成功...这个异常控制可能还需要从模型那层扔到controller这层来.因为需要根据是否发生异常来返回不同的提示信息.
		try{
			model.saveModel();
			return jsonDump(SUCCESS);
		}catch(Exception e){
			logException(e);
			return jsonDump(ERROR);
		}
	}
	@RequestMapping(value = "/classify")
	public String classification(HttpServletRequest req) throws InterruptedException {
		String operationType = req.getParameter("operationtype");
		if (Integer.parseInt(operationType) == 1) { // 1是预测
			model.restoreFromFile();
			String doctype = req.getParameter("doctype");
			if (doctype == null || Integer.parseInt(doctype) == 1) {
				String str = req.getParameter("string");
				return jsonDump(model.predictDocumentLabelString(str));
			} else if (Integer.parseInt(doctype) == 1) {
				return jsonDump(model.predictDocument(""));
			}
		} else if (Integer.parseInt(operationType) == 2) { // 2是训练模型
			String dataType = req.getParameter("datatype");
			decideWhichData(dataType == null ? 1 : Integer.parseInt(dataType));
			model.buildNetworkModel();
			TimeUnit.SECONDS.sleep(100);
			model.saveModel();
			return jsonDump(SUCCESS);
		}

		return jsonDump(ERROR);
	}

	public void decideWhichData(int datatype) {
		model.configNetworkDesignHandler(this);
		model.configDataSetIteratorHandlerProvider(this);
	}
	@AsyncInvoke(by="TextCategorizationCNNModel中线程池中的线程")
	@Override
	public ComputationGraphConfiguration handleCGC(int embeddingWordVectorLength) {
		int vectorSize = embeddingWordVectorLength;
		int cnnLayerFeatureMaps = config.getCNNLayerFeatureMaps();
		ComputationGraphConfiguration config = new NeuralNetConfiguration.Builder()
	            .weightInit(WeightInit.RELU)
	            .activation(Activation.LEAKYRELU)
	            .updater(Updater.ADAM)
	            .convolutionMode(ConvolutionMode.Same)      //This is important so we can 'stack' the results later
	            .regularization(true).l2(0.0001)
	            .learningRate(0.01)
	            .graphBuilder()
	            .addInputs("input")
	            .addLayer("cnn3", new ConvolutionLayer.Builder()
	                .kernelSize(3,vectorSize)
	                .stride(1,vectorSize)
	                .nIn(1)
	                .nOut(cnnLayerFeatureMaps)
	                .build(), "input")
	            .addLayer("cnn4", new ConvolutionLayer.Builder()
	                .kernelSize(4,vectorSize)
	                .stride(1,vectorSize)
	                .nIn(1)
	                .nOut(cnnLayerFeatureMaps)
	                .build(), "input")
	            .addLayer("cnn5", new ConvolutionLayer.Builder()
	                .kernelSize(5,vectorSize)
	                .stride(1,vectorSize)
	                .nIn(1)
	                .nOut(cnnLayerFeatureMaps)
	                .build(), "input")
	            .addVertex("merge", new MergeVertex(), "cnn3", "cnn4", "cnn5")      //Perform depth concatenation
	            .addLayer("globalPool", new GlobalPoolingLayer.Builder()
	                .poolingType(this.config.getPoolingType())
	                .build(), "merge")
	            .addLayer("out", new OutputLayer.Builder()
	                .lossFunction(LossFunctions.LossFunction.MCXENT)
	                .activation(Activation.SOFTMAX)
	                .nIn(3*cnnLayerFeatureMaps)
	                .nOut(20)    //2 classes: positive or negative
	                .build(), "globalPool")
	            .setOutputs("out")
	            .build();
	 return config;
	}
	@AsyncInvoke(by="TextCategorizationCNNModel中线程池中的线程")
	@Override
	public MultiLayerConfiguration handleMLN(int embeddingWordVectorLength) {
		return null;
	}

}